/*********************************************************
 * Project Atlantis Page Table Virtual Address Manager   *
 * This code is largely based on or copied from the      *
 * BrokenThorn tutorial 17, because the programmer was   *
 * too lazy to write his own.						     *
 *********************************************************/
#include "dbgio.h"
#include "vmm_ptm.h"
#include "vmm.h" // virtual_addr type
uint32_t  ptm_used_blocks;
uint32_t  ptm_max_blocks;
uint32_t* ptm_block_map;

inline void ptm_map_set(uint32_t bit){
	ptm_block_map[bit / 32] |= 1 << (bit % 32);
}

inline void ptm_map_clear(uint32_t bit){
	ptm_block_map[bit / 32] &= ~ (1 << (bit % 32));
}

inline char ptm_map_test(uint32_t bit){
	return (char) (ptm_block_map[bit / 32] & (1 << (bit % 32)));
}

int ptm_map_ffree() {
	int bit;
	int j;
	uint32_t i;
	for (i=0; i < (ptm_max_blocks / 32); i++)
		if (ptm_block_map[i] != 0xffffffff)
			for (j=0; j<32; j++) {
				bit = 1 << j;
				if (! (ptm_block_map[i] & bit) )
					return i*4*8+j;
			} 
	return -1;
}

int ptm_map_ffree_s (size_t size) {
	int startingBit;
	int j;
	int bit;
	uint32_t i;
    uint32_t free;
	uint32_t count;
	if (size==0)
		return -1;
	if (size==1)
		return ptm_map_ffree();
	for (i=0; i<(ptm_max_blocks /32); i++)
		if (ptm_block_map[i] != 0xffffffff)
			for (j=0; j<32; j++) {	//! test each bit in the dword
				bit = 1<<j;
				if (!(ptm_block_map[i] & bit) ) {
					startingBit = i*32;
					startingBit+=bit;		//get the free bit in the dword at index i
					free=0; //loop through each bit to see if its enough space
					for (count=0; count<=size;count++) {
						if (! ptm_map_test (startingBit+count) )
							free++;	// this bit is clear (free frame)
						if (free==size)
							return i*4*8+j; //free count==size needed; return index
					}
				}
			}
	return -1;
}

uint32_t ptm_get_free_block_count(){
	return ptm_max_blocks - ptm_used_blocks;
}
uint32_t ptm_get_size(){
	return ptm_max_blocks / 8;
}

void ptm_init (virtual_addr bitmap) { 
	ptm_block_map	=	(uint32_t*) bitmap;
	ptm_max_blocks	=	PTM_MAX_PTS * 1025;//1025 is the size of a full page dire in blocks
	ptm_used_blocks	=	0x00;
	dprintf("VAL 0x%x 0x%x\n",bitmap,&ptm_block_map);
	memset (ptm_block_map, 0x00, ptm_max_blocks / 8);
}

void* ptm_alloc_block() { 
	int frame;
	virtual_addr addr;
	if (ptm_get_free_block_count() <= 0)
		return 0;	//out of memory 
	frame = ptm_map_ffree(); 
	if (frame == -1)
		return 0;	//out of memory 
	ptm_map_set(frame); 
	addr = (frame * PTM_BLOCK_SIZE) + PTM_PTS_START;
	ptm_used_blocks++; 
	return (void*)addr;
}

void* ptm_alloc_blocks(size_t size) { 
	uint32_t count;
	int frame;
	virtual_addr addr;
	if (ptm_get_free_block_count() <= size)
		return 0;	//out of memory 
	frame = ptm_map_ffree_s(size); 
	if (frame == -1)
		return 0;	//out of memory 
	for (count = 0;count < size; count++)
		ptm_map_set(frame+count); 
	addr = (frame * PTM_BLOCK_SIZE) + PTM_PTS_START;
	ptm_used_blocks+=size; 
	return (void*)addr;
}

void ptm_free_block (void* p) { 
	virtual_addr addr = (virtual_addr)p;
	int frame = (addr - PTM_PTS_START) / PTM_BLOCK_SIZE; 
	ptm_map_clear(frame); 
	ptm_used_blocks--;
} 
void ptm_free_blocks(void* p, size_t size) { 
	uint32_t count;
	virtual_addr addr = (virtual_addr)p;
	int frame = (addr - PTM_PTS_START) / PTM_BLOCK_SIZE; 
	for (count = 0;count < size; count++)
		ptm_map_clear(frame+count); 
	ptm_used_blocks-=size;
}